package usercenter

import (
	"context"
	"database/sql"
	"fmt"
	"github.com/zeromicro/go-zero/core/stores/cache"
	"github.com/zeromicro/go-zero/core/stores/sqlx"
	"navi-mumbai/common/gensql"
	"time"
)

var _ UserAccountModel = (*customUserAccountModel)(nil)

type (
	// UserAccountModel is an interface to be customized, add more methods here,
	// and implement the added methods in customUserAccountModel.
	UserAccountModel interface {
		userAccountModel
		InsertTrans(ctx context.Context, tx sqlx.Session, data *UserAccount) (sql.Result, error)
		Trans(ctx context.Context, fn func(context context.Context, tx sqlx.Session) error) error
		Find(ctx context.Context, deviceId string, start, end *time.Time, page, pageSize int64) ([]*UserAccount, error)
		Count(ctx context.Context, start, end *time.Time) (int64, error)
		UpdateAccountState(ctx context.Context, tx sqlx.Session, uid int64, authKey string, authType int64, accountState int64) error
		FindFirstUid(ctx context.Context, start *time.Time, end *time.Time) (int64, error)
		FindLastUid(ctx context.Context, start *time.Time, end *time.Time) (int64, error)
		FindLMaxUid(ctx context.Context) (int64, error)
	}

	customUserAccountModel struct {
		*defaultUserAccountModel
		genSql *gensql.GenSql
	}
)

// NewUserAccountModel returns a model for the database table.
func NewUserAccountModel(conn sqlx.SqlConn, c cache.CacheConf, opts ...cache.Option) UserAccountModel {
	return &customUserAccountModel{
		defaultUserAccountModel: newUserAccountModel(conn, c, opts...),
		genSql:                  gensql.NewGenSql(),
	}
}

func (m *customUserAccountModel) Trans(ctx context.Context, fn func(ctx context.Context, tx sqlx.Session) error) error {
	return m.TransactCtx(ctx, func(ctx context.Context, tx sqlx.Session) error {
		return fn(ctx, tx)
	})
}

func (m *customUserAccountModel) InsertTrans(ctx context.Context, tx sqlx.Session, data *UserAccount) (sql.Result, error) {
	mumbaiUsercenterUserAccountAuthKeyKey := fmt.Sprintf("%s%v", cacheMumbaiUsercenterUserAccountAuthKeyAuthTypePrefix, data.AuthKey)
	mumbaiUsercenterUserAccountUidKey := fmt.Sprintf("%s%v", cacheMumbaiUsercenterUserAccountUidPrefix, data.Uid)
	ret, err := m.ExecCtx(ctx, func(ctx context.Context, conn sqlx.SqlConn) (result sql.Result, err error) {
		query := fmt.Sprintf("insert into %s (%s) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", m.table, userAccountRowsExpectAutoSet)
		return tx.ExecCtx(ctx, query, data.Uid, data.AuthKey, data.Userpass, data.AuthType, data.AccountState, data.UserType, data.UserChannel, data.RegIp,
			data.RegDevice, data.RegDeviceId, data.AppsFlyerUid)
	}, mumbaiUsercenterUserAccountAuthKeyKey, mumbaiUsercenterUserAccountUidKey)

	return ret, err
}

func (m *customUserAccountModel) Find(ctx context.Context, deviceId string, start, end *time.Time, page, pageSize int64) ([]*UserAccount, error) {
	q := fmt.Sprintf("select %s from %s ", userAccountRows, m.table)
	m.genSql.InitSql(q)

	if start != nil {
		m.genSql = m.genSql.Where("create_time >= ?", start)
	}
	if end != nil {
		m.genSql = m.genSql.Where("create_time <= ?", end)
	}
	if deviceId != "" {
		m.genSql = m.genSql.Where("reg_device_id = ?", deviceId)
	}

	// 分页
	if page < 1 {
		page = 1
	}
	if pageSize <= 0 {
		pageSize = 10
	}

	m.genSql = m.genSql.OrderBy("create_time desc").Limit(uint64(pageSize)).Offset(uint64((page - 1) * pageSize))

	query, values, err := m.genSql.ToSql()
	if err != nil {
		return nil, err
	}
	resp := make([]*UserAccount, 0)
	err = m.QueryRowsNoCacheCtx(ctx, &resp, query, values...)
	switch err {
	case nil:
		return resp, nil
	default:
		return nil, err
	}
}

func (m *customUserAccountModel) Count(ctx context.Context, start, end *time.Time) (int64, error) {
	q := fmt.Sprintf("select count(uid) from %s ", m.table)
	m.genSql.InitSql(q)

	if start != nil {
		m.genSql = m.genSql.Where("create_time >= ?", start)
	}
	if end != nil {
		m.genSql = m.genSql.Where("create_time <= ?", end)
	}

	query, values, err := m.genSql.ToSql()
	if err != nil {
		return 0, err
	}
	var resp int64
	err = m.QueryRowNoCacheCtx(ctx, &resp, query, values...)
	switch err {
	case nil:
		return resp, nil
	default:
		return 0, err
	}
}

func (m *customUserAccountModel) UpdateAccountState(ctx context.Context, tx sqlx.Session, uid int64, authKey string, authType int64, accountState int64) error {
	mumbaiUsercenterUserAccountAuthKeyKey := fmt.Sprintf("%s%v:%v", cacheMumbaiUsercenterUserAccountAuthKeyAuthTypePrefix, authKey, authType)
	mumbaiUsercenterUserAccountUidKey := fmt.Sprintf("%s%v", cacheMumbaiUsercenterUserAccountUidPrefix, uid)
	_, err := m.ExecCtx(ctx, func(ctx context.Context, conn sqlx.SqlConn) (result sql.Result, err error) {
		query := fmt.Sprintf("update %s set account_state = ? where uid = ?", m.table)
		return tx.ExecCtx(ctx, query, accountState, uid)
	}, mumbaiUsercenterUserAccountAuthKeyKey, mumbaiUsercenterUserAccountUidKey)
	if err != nil {
		return err
	}
	return nil
}

func (m *customUserAccountModel) FindFirstUid(ctx context.Context, start *time.Time, end *time.Time) (int64, error) {
	query := fmt.Sprintf("select uid from %s ", m.table)
	m.genSql.InitSql(query)
	q, a, err := m.genSql.Where("create_time between ? and ?", start, end).OrderBy("create_time").Limit(1).ToSql()
	if err != nil {
		return 0, err
	}
	var uid int64
	err = m.QueryRowNoCacheCtx(ctx, &uid, q, a...)
	switch err {
	case nil:
		return uid, nil
	default:
		return 0, err
	}
}

func (m *customUserAccountModel) FindLastUid(ctx context.Context, start *time.Time, end *time.Time) (int64, error) {
	query := fmt.Sprintf("select uid from %s ", m.table)
	m.genSql.InitSql(query)
	q, a, err := m.genSql.Where("create_time between ? and ?", start, end).OrderBy("create_time DESC").Limit(1).ToSql()
	if err != nil {
		return 0, err
	}
	var uid int64
	err = m.QueryRowNoCacheCtx(ctx, &uid, q, a...)
	switch err {
	case nil:
		return uid, nil
	default:
		return 0, err
	}
}

func (m *customUserAccountModel) FindLMaxUid(ctx context.Context) (int64, error) {
	query := fmt.Sprintf("select COALESCE(Max(uid),1) from %s ", m.table)
	var uid int64
	err := m.QueryRowNoCacheCtx(ctx, &uid, query)
	switch err {
	case nil:
		return uid, nil
	default:
		return 0, err
	}
}
